var TheGame = require('geoTheGame');
var Utils = require('geoUtils');
var EventTypes = require('geoEventTypes');
var LevelTransit = require('geoTransitBase');

var LevelData = cc.Class({
    name: 'geoLevelData',
    properties: {
        levelPrefab: {
            default: null,
            type: cc.Prefab,
            tooltip: '关卡预制件'
        },
        levelEnterPrefab: {
            default: null,
            type: cc.Prefab,
            tooltip: '进入此关卡时入场预制件'
        },
        levelEndPassPrefab: {
            default: null,
            type: cc.Prefab,
            tooltip: '此关卡成功通过时预制件'
        },
        levelEndFailPrefab: {
            default: null,
            type: cc.Prefab,
            tooltip: '此关卡失败时预制件'
        },
    }
});

var GameMain = cc.Class({
    extends: TheGame,

    properties: {
        titlePrefab: {
            default: null,
            type: cc.Prefab,
            tooltip: '标题背景预制体（可留空）',
        },
        cutScenePlayNode: {
            default: null,
            type: cc.Node,
            tooltip: '用来承载开场及结局的节点',
        },
        openningPrefab: {
            default: null,
            type: cc.Prefab,
            tooltip: '开场预制体'
        },
        endingPrefab: {
            default: null,
            type: cc.Prefab,
            tooltip: '结束预制体'
        },
        levelPlayNode: {
            default: null,
            type: cc.Node,
            tooltip: '用来承载关卡的节点',
        },
        levelDatas: {
            default: [],
            type: LevelData,
            tooltip: '关卡数组',
        },
        maxLevels: {
            default: -1,
            tooltip: '最大关卡数量，-1即使用所有关卡',
        },
        useRandomLevels: {
            default: true,
            tooltip: '是否对关卡乱序',
        },
        fadeMaskColor: {
            default: cc.Color.BLACK,
            tooltip: '关卡切换时渐隐渐出的颜色'
        },
        fadeMaskDuration: {
            default: 0.3,
            type: cc.Float,
            max: 1.0,
            min: 0.0,
            tooltip: '切换所需时长'
        },
        levelTransitPrefab: {
            default: null,
            type: cc.Prefab,
            tooltip: '关卡切换预制'
        },
        ignoreLevelCutScene:{
            default: false,
            tooltip: '为true时，忽略所有关卡过场。'
        },
        debugMode: {
            default: false,
            tooltip: '为true时会省略开场动画以及关卡过场，适用于调试阶段。'
        }
    },

    // LIFE-CYCLE CALLBACKS:

    // onLoad () {},

    /**
     * 子类重载该方法完成定制初始化。
     * 子类重载需要先调用父类方法。
     */
    start() {
        // 进行必要的模式准备，比如去除开场动画或者关卡过场。
        if (this.debugMode === true)
            this._useDebugMode();
            
        if (this.ignoreLevelCutScene === true)
            this._useIgnoreLevelCutSceneMode();
        // 进行常规初始化
        // 当前得分
        this._score = 0;
        // 对已经通过的关卡进行计数
        this._numLevelsPassed = 0;
        // 确定关卡的数量
        if (this.maxLevels <= 0)
            this.maxLevels = this.levelDatas.length;
        // 保留原始关卡设定
        this._originLevelDatas = this.levelDatas.concat();
        // 乱序关卡预制体
        if (this.useRandomLevels === true)
            this.levelDatas = Utils.array.random(this.levelDatas, this.maxLevels, true);
        // 显示封面预制体
        this._showTitle(true);
        // 
        this._levelTransit = this._getLevelTransit();
    },
    /**
     * 子类重载该方法，子类代码逻辑从这里开始。
     */
    startGame() {
        // 同步一下已通过关卡数量，供外部逻辑使用
        this._syncNumLevelsPassed();
        //
        this._fadeInOut((() => {
            // 销毁title
            this._showTitle(false);
            //
            if (this.openningPrefab != null) {
                // 发现开场内容，先播放开场
                this.playOpenning();
            } else {
                this.checkGameOver();
            }
        }).bind(this));
    },
    /**
     * 开场逻辑完毕时调用。
     * 子类可重载并调用父类方法。
     * 需要调用this._super()
     */
    onOpenningEnd() {
        this._fadeInOut((() => {
            // 销毁当前cutscene
            this._currentCutScene.destroy();
            this._currentCutScene = null;
            //
            this.checkGameOver();
        }).bind(this));
    },
    /**
     * 结局逻辑完毕时调用
     * 子类可重载并调用父类方法。
     * 要调用this._super()
     */
    onEndingEnd() {
        // this._fadeInOut((() => {
        //     // 销毁当前cutscene
        //     this._currentCutScene.destroy();
        //     this._currentCutScene = null;
        //     //
        //     this.doGameOver();
        // }).bind(this));

        // 终场不再需要淡入淡出，也不需要销毁过场
        this.doGameOver();
    },
    /**
     * 获取下一关的关卡数据。
     * 默认按照（乱序后的）顺序选取下一关
     * 子类可重写该方法已定制关卡选择策略
     * @param {[]} levelDatas 关卡数据数组
     * @param {Number} numLevelsPassed 已经经过的关卡数量
     * @param {*} lastLevelData 上一关关卡数据
     * @param {[]} originLevelDatas 原始关卡设定（未被乱序的）
     */
    getNextLevelData(levelDatas, numLevelsPassed, lastLevelData, originLevelDatas) {
        return levelDatas[numLevelsPassed];
    },
    /**
     * 开始下一关
     */
    goNextLevel() {
        // 获取关卡数据
        // var levelData = this.levelDatas[this._numLevelsPassed];
        var levelData = this.getNextLevelData(
            this.levelDatas, this._numLevelsPassed, this._currentLevelData, this._originLevelDatas);
        this._currentLevelData = levelData;
        // 定义关卡逻辑执行方法
        var runLevelFunc = () => {
            this._currentLevel = cc.instantiate(this._currentLevelData.levelPrefab);
            this.getLevelNode().addChild(this._currentLevel);
            // 为关卡运行前做准备
            this.prepareLevel(this._currentLevel, this._numLevelsPassed, this._score);
            // 注册关卡结束事件
            this._currentLevel.once(EventTypes.LevelEnd, this.onLevelEnd.bind(this));
            // 关卡入场切换
            // this._levelTransit.setTransitTarget(this._currentLevel);
            // this._levelTransit.enter(() => { cc.log('入场切换完成') });
            this._transitLevelEnter(this._currentLevel, ()=>{cc.log('关卡入场切换完成')});
            // 为关卡关联gameMaster对象
            var levelCom = this._currentLevel.getComponent('geoGameLevel');
            levelCom && (levelCom.gameMaster = this.gameMaster);
        }
        // 若有进场则先播放进场
        if (levelData.levelEnterPrefab != null) {
            this._playCutScene(levelData.levelEnterPrefab, EventTypes.CutSceneEnd, (() => {
                this._fadeInOut((() => {
                    // 销毁过场
                    this._destroyCutScene();
                    // 开始关卡逻辑
                    runLevelFunc();
                }).bind(this));
            }).bind(this));
        } else {
            runLevelFunc();
        }
    },
    /**
     * 当关卡逻辑结束时被调用。
     * 判定进行下一关还是结束游戏。
     */
    onLevelEnd() {
        // 已经过关卡数量计数
        ++this._numLevelsPassed;
        this._syncNumLevelsPassed();
        // 读取关卡结束数据
        var data = this.gameMaster.data.levelEnd;
        //
        var levelData = this._currentLevelData;
        //
        // this.scheduleOnce(this._onLevelEndAction.bind(this, data, levelData), data.delay);
        // return;
        // 由主逻辑做一些关卡结束的处理
        this.postLevel(this._score, this._numLevelsPassed, data.result, this._currentLevel);
        // 处理关卡结果
        var scoreCallbackAction = (() => {
            // 处理关卡切出
            // this._levelTransit.setTransitTarget(this._currentLevel);
            // this._levelTransit.leave();
            this._transitLevelLeave(this._currentLevel, (() => {
                if (data.destroy == true && this._shouldDestroyLevel() == true)
                    this._currentLevel.destroy();
                this._currentLevel = null;
                // 处理关卡离去
                var cutScenePrefab = null;
                if (data.result == true)
                    cutScenePrefab = levelData.levelEndPassPrefab;
                else
                    cutScenePrefab = levelData.levelEndFailPrefab;
                if (cutScenePrefab != null) {
                    this._playCutScene(cutScenePrefab, EventTypes.CutSceneEnd, (() => {
                        if (this._shouldDestroyLevelEndCutScene() == true) {
                            // 渐变，销毁关卡结尾过场
                            this._fadeInOut((() => {
                                this._destroyCutScene();
                                //
                                this.checkGameOver();
                            }).bind(this));
                        } else {
                            // 不效果关卡结尾过场
                            this.checkGameOver();
                        }
                    }).bind(this))
                } else {
                    // 检测游戏结束
                    this.checkGameOver();
                }
            }).bind(this));
        }).bind(this);
        var scoreCallback = (() => {
            if (this._shouldDestroyLevel())
                scoreCallbackAction = this._fadeInOut.bind(this, scoreCallbackAction);
            // 根据延时处理调用
            this.scheduleOnce(scoreCallbackAction, data.delay);
        }).bind(this);
        //
        if (data.handleScore == true) {
            if (data.result == true) {
                ++this._score;
                this.gameMaster.setTopBarScore(this._score, scoreCallback);
            } else {
                this.gameMaster.loseTopBarScore([this._score + 1], scoreCallback);
            }
        } else {
            scoreCallback();
        }
    },
    /**
     * 新关卡刚刚被创建时调用，可以做一些准备工作
     * @param {int} score 当前积分
     * @param {int} numLevelsPassed 当前已经历关卡数量
     * @param {cc.Node} level 刚刚被创建的关卡节点
     */
    prepareLevel(score, numLevelsPassed, level) {

    },
    /**
     * 关卡结束后处理，主逻辑可通过重载该方法完成关卡结束后的定制操作
     * @param {int} score 当前积分。
     * @param {int} numLevelsPassed 当前已经历关卡数量。
     * @param {bool} result 当前结束关卡的成绩（是否正确）。
     * @param {cc.Node} level 刚刚结束的关卡节点
     */
    postLevel(score, numLevelsPassed, result, level) {

    },
    /**
     * 检测游戏结束
     */
    checkGameOver() {
        if (this.hasNextLevel() == false) {
            // 已达到最大关卡数量，考虑游戏结束
            if (this.endingPrefab != null)
                this.playEnding();
            else
                this.doGameOver();
        } else {
            // 开启下一关
            this.goNextLevel();
        }
    },
    /**
     * 是否还有下一关
     * @returns {boolean} true，有下一关，false，没有下一关
     */
    hasNextLevel() {
        return !(this._numLevelsPassed == this.maxLevels);
    },
    /**
     * 处理游戏结束
     */
    doGameOver() {
        this.gameMaster.showResultDialog(true);
        this.gameMaster.setResultScore(this._score);
    },
    playOpenning() {
        this._playCutScene(this.openningPrefab, EventTypes.CutSceneEnd, this.onOpenningEnd.bind(this));
    },
    playEnding() {
        this._playCutScene(this.endingPrefab, EventTypes.CutSceneEnd, this.onEndingEnd.bind(this));
    },
    _playCutScene(cutScenePrefab, eventType, callback) {
        // 产生开场内容
        var n = cc.instantiate(cutScenePrefab);
        // 获取过场节点
        var cutSceneNode = this.getCutSceneNode();
        //
        cutSceneNode.addChild(n);
        // 注册结束监听
        callback && n.once(eventType, callback);
        // 记录当前cutscene
        this._currentCutScene = n;
    },
    _destroyCutScene() {
        if (this._currentCutScene)
            this._currentCutScene.destroy();
        this._currentCutScene = null;
    },
    getCutSceneNode() {
        return this.cutScenePlayNode;
    },
    getLevelNode() {
        return this.levelPlayNode;
    },
    _fadeIn(complete) {
        this.gameMaster.ui.fadeMaskIn(this.fadeMaskDuration, this.fadeMaskColor, complete);
    },
    _fadeOut(complete) {
        this.gameMaster.ui.fadeMaskOut(this.fadeMaskDuration, this.fadeMaskColor, complete);
    },
    _fadeInOut(complete) {
        this.gameMaster.ui.fadeMaskInOut(this.fadeMaskDuration, this.fadeMaskColor, complete);
    },
    _syncNumLevelsPassed() {
        this.gameMaster.data.numLevelsPassed = this._numLevelsPassed;
    },
    _showTitle(bShow) {
        // TODO 临时使用如下ui方式，以后应解决此处无法获取ui的问题
        var _ui = require('geoUIManager').Singleton;
        if (bShow === true) {
            this.titlePrefab && (this._title = _ui.open(this.titlePrefab, false, this.levelPlayNode));
        } else {
            this._title && (_ui.close(this._title));
            this._title = null;
        }
    },
    _shouldDestroyLevel() {
        // 不是最后一关 或者 有游戏结束过场 或者 有关卡结束过场
        if (this.hasNextLevel()
            || this._currentLevelData.levelEndPassPrefab
            || this._currentLevelData.levelEndFailPrefab
            || this.endingPrefab) {
            return true;
        } else {
            return false;
        }
    },
    _shouldDestroyLevelEndCutScene() {
        // 不是最后一关 或者 有游戏结束过场
        if (this.hasNextLevel()
            || this.endingPrefab) {
            return true;
        } else {
            return false;
        }
    },
    _transitLevel(transFunc, levelNode, complete){
        // 获取可能存在的widget，禁用
        var widget = levelNode.getComponent(cc.Widget);
        widget && (widget.enabled = false);
        this._levelTransit.setTransitTarget(levelNode);
        transFunc(()=>{
            // 启用widget
            widget && (widget.enabled = true);
            //
            complete && complete();
        });
    },
    _transitLevelEnter(levelNode, complete){
        this._transitLevel(this._levelTransit.enter.bind(this._levelTransit), levelNode, complete);
    },
    _transitLevelLeave(levelNode, complete){
        // 如果是最后一关就不转场了
        if (this._shouldDestroyLevel() == true)
            this._transitLevel(this._levelTransit.leave.bind(this._levelTransit), levelNode, complete);
        else {
            complete && complete();
        }
    },
    _getLevelTransit() {
        var levelTransit = null;
        if (this.levelTransitPrefab) {
            levelTransit = cc.instantiate(this.levelTransitPrefab);
            levelTransit.parent = this.getLevelNode();
            levelTransit = levelTransit.getComponent(LevelTransit);
        }
        //
        if (levelTransit == null) levelTransit = this.addComponent(LevelTransit);
        //
        return levelTransit;
    },
    _useDebugMode() {
        // 删除所有过场
        this.openningPrefab = null;
        this.endingPrefab = null;
        //
        this._useIgnoreLevelCutSceneMode();
    },
    _useIgnoreLevelCutSceneMode(){
        //
        this.levelDatas.forEach(levelData => {
            levelData.levelEnterPrefab = null;
            levelData.levelEndPassPrefab = null;
            levelData.levelEndFailPrefab = null;
        });
    }
});


module.exports = GameMain;